/*  Copyright (c) 2012 William Rogers and James Boud

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

using System;
using SurfaceFormat = Microsoft.Xna.Framework.Graphics.SurfaceFormat;
using DepthFormat = Microsoft.Xna.Framework.Graphics.DepthFormat;
using RenderTargetUsage = Microsoft.Xna.Framework.Graphics.RenderTargetUsage;
using SlimDX;
using SlimDX.Direct3D11;

namespace JBBRXG11
{
    /// <summary>
    /// Represents a cubic texture resource that will be written to at the end of a
    /// render pass.
    /// </summary>
    public class RenderTargetCube : TextureCube, IDisposable
    {
        RenderTargetView[] dxtargetview;
        Viewport myviewport;
        // For the moment one depth buffer is shared by all faces.  It would be nice to be able to write to
        // all faces in a single pass, but as I understand the limitations of DX11, it is not possible to 
        // assign multiple depth buffers to the output merger stage, to multiple face writing cannot be done
        // with independent depth testing ???
        SlimDX.Direct3D11.Texture2D dxdepthbuffer;
        SlimDX.Direct3D11.DepthStencilView dxdepthstencilview;

        internal RenderTargetView GetRenderTargetView(Microsoft.Xna.Framework.Graphics.CubeMapFace cubeMapFace)
        {
            return dxtargetview[(int)cubeMapFace];
        }

        internal Viewport RenderViewport { get { return myviewport; } }
        internal SlimDX.Direct3D11.DepthStencilView DX_DepthStencilView { get { return dxdepthstencilview; } }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////// Constructors ////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////

        void MakeTargetViews(GraphicsDevice graphicsDevice)
        {
            dxtargetview = new RenderTargetView[6];
            RenderTargetViewDescription description = new RenderTargetViewDescription();
            for (int s = 0; s < 6; s++)
            {
                description.Format = dxtexture.Description.Format;
                description.Dimension = RenderTargetViewDimension.Texture2DArray;
                description.FirstArraySlice = (int)s;
                description.ArraySize = 1;
                description.DepthSliceCount = 0;
                description.ElementOffset = 0;
                description.ElementWidth = 4;
                description.FirstDepthSlice = 0;
                description.MipSlice = 0;
                dxtargetview[s] = new RenderTargetView(graphicsDevice.DX_Device, dxtexture, description);
            }
            myviewport = new Viewport(0, 0, Size, Size);
        }

        /// <summary>
        /// Creates an instance of this object.
        /// </summary>
        /// <param name="graphicsDevice">The graphics device to associate with this
        /// render target resource.</param>
        /// <param name="size">The width and height of this cube texture resource, in
        /// pixels.</param>
        /// <param name="mipMap">[MarshalAsAttribute(U1)] True to generate a full
        /// mipmap chain, false otherwise.</param>
        /// <param name="preferredFormat">Preferred format of the surface data.
        /// </param>
        /// <param name="preferredDepthFormat">Preferred format of the depth data.
        /// </param>
        /// <param name="preferredMultiSampleCount">Preferred number of sample
        /// locations during multisampling.</param>
        /// <param name="usage">Options identifying the behaviors of this texture
        /// resource.</param>
        public RenderTargetCube(GraphicsDevice graphicsDevice, int size, bool mipMap, SurfaceFormat preferredFormat, DepthFormat preferredDepthFormat, int preferredMultiSampleCount, RenderTargetUsage usage)
            : base(graphicsDevice, size, mipMap, preferredFormat, true)
        {
            MakeTargetViews(graphicsDevice);
            RenderTarget2D.MakeDepthBuffer(graphicsDevice, size, size, preferredDepthFormat, out dxdepthbuffer, out dxdepthstencilview);
            GraphicsDeviceManager.GetGame(Game.GraphicsDevice).UserContentAdd(this);
        }

        /// <summary>
        /// Creates an instance of this object.
        /// </summary>
        /// <param name="graphicsDevice">The graphics device to associate with this
        /// render target resource.</param>
        /// <param name="size">The width and height of this cube texture resource, in
        /// pixels.</param>
        /// <param name="mipMap">[MarshalAsAttribute(U1)] True to generate a full
        /// mipmap chain, false otherwise.</param>
        /// <param name="preferredFormat">Preferred format of the surface data.
        /// </param>
        /// <param name="preferredDepthFormat">Preferred format of the depth data.
        /// </param>
        public RenderTargetCube(GraphicsDevice graphicsDevice, int size, bool mipMap, SurfaceFormat preferredFormat, DepthFormat preferredDepthFormat)
            : base(graphicsDevice, size, mipMap, preferredFormat, true)
        {
            MakeTargetViews(graphicsDevice);
            RenderTarget2D.MakeDepthBuffer(graphicsDevice, size, size, preferredDepthFormat, out dxdepthbuffer, out dxdepthstencilview);
            GraphicsDeviceManager.GetGame(Game.GraphicsDevice).UserContentAdd(this);
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////// Properties //////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Determines if the data has been lost due to a lost device event.
        /// </summary>
        public bool IsContentLost { get { throw new Exception("NYI"); } }

        /// <summary>
        /// Gets the usage mode of this rendertarget.
        /// </summary>
        public RenderTargetUsage RenderTargetUsage { get { throw new Exception("NYI"); } }

        /// <summary>
        /// Gets the number of multisample locations.
        /// </summary>
        public int MultiSampleCount { get { throw new Exception("NYI"); } }

        /// <summary>
        /// Gets the depth format of this rendertarget.
        /// </summary>
        public DepthFormat DepthStencilFormat { get { throw new Exception("NYI"); } }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////// Methods /////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public virtual void add_ContentLost(EventHandler<EventArgs> value)
        {
            throw new Exception("Not Yet Implemented");
        }

        public virtual void remove_ContentLost(EventHandler<EventArgs> value)
        {
            throw new Exception("Not Yet Implemented");
        }

        protected virtual void raise_ContentLost(Object value0, EventArgs value1)
        {
            throw new Exception("Not Yet Implemented");
        }

        public new void Dispose()
        {
            if (dxdepthstencilview != null) dxdepthstencilview.Dispose();
            if (dxdepthbuffer != null) dxdepthbuffer.Dispose();
            for (int s = 0; s < 6; s++)
                dxtargetview[s].Dispose();
            base.Dispose();
        }
    }
}
